<?php
/**
 *
 * 中文分词算法 http://phpman.cn/so/
 * @author lgm
 * 加上新词识别
 */

$settings_dir = "./settings";
require ($settings_dir."/database.php");

class  SW {

    /**
     * 要分词的字符串
     */
    public $str = '';

    /**
     * 要使用的算法
     * 0: 正向最大匹配算法
     * 1: 逆向最大匹配算法
     * 2: 双向最大匹配算法
     */
    public $algorithm = 2;

    /**
     * 文本字符集 默认UTF-8
     */
    public $encode = 'UTF-8';

    /**
     * mysql 参数
     * array 主机地址,数据库名,账号,密码
     */
    public $db_param;

    /**
     * 词表
     */
    const TB_DICT = 'words';

    /**
     * 停用词词表
     */
    const TB_DICT_STOP = 'stop_words';

    /**
     * 姓氏词表
     */
    const TB_DICT_FAMILY_NAME = 'family_name';

    /**
     * 字典中最大词长度
     */
    const MAXLEN = 4;

    /**
     * 基本单位词
     */
    private $unit_arr;

    /**
     * 句子分隔符
     */
    private $split_arr;

    /**
     *  关键字数组
     */
    public $keywords = array();

    /**
     * 字符串长度
     */
    private $length = 0;

    /**
     * 双字姓氏
     */
    private $two_name = array();

    /**
     * 单子姓氏
     */
    private $one_name = array();

    /**
     * 初始化数据
     */
    public function __construct()
    {
        $this->keywords = array();
        $this->unit_arr = array('年','月','日','时','分','秒','点','元','百','千','万','亿','位','辆');
        $this->split_arr = array(',','.','?','!',';', '，','。','？','！','；',' ',' ');
        $this->two_name = array("端木","南宫","谯笪","轩辕","令狐","钟离","闾丘","长孙","鲜于","宇文","司徒","司空","上官","欧阳","公孙","西门","东门","左丘","东郭","呼延","慕容","司马","夏侯","诸葛","东方","赫连","皇甫","尉迟","申屠");
        $this->one_name = str_split("赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛奚范彭郎鲁韦昌马苗凤花方俞任袁柳酆鲍史唐费廉岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅皮卡齐康伍余元卜顾孟平黄穆萧尹姚邵堪汪祁毛禹狄米贝明臧计伏成戴谈宋茅庞熊纪舒屈项祝董粱杜阮蓝闵席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田樊胡凌霍虞万支柯咎管卢莫经房裘缪干解应宗宣丁贲邓郁单杭洪包诸左石崔吉钮龚程嵇邢滑裴陆荣翁荀羊於惠甄魏加封芮羿储靳汲邴糜松井段富巫乌焦巴弓牧隗谷车侯宓蓬全郗班仰秋仲伊宫宁仇栾暴甘钭厉戎祖武符刘姜詹束龙叶幸司韶郜黎蓟薄印宿白怀蒲台从鄂索咸籍赖卓蔺屠蒙池乔阴郁胥能苍双闻莘党翟谭贡劳逄姬申扶堵冉宰郦雍郤璩桑桂濮牛寿通边扈燕冀郏浦尚农温别庄晏柴翟阎充慕连茹习宦艾鱼容向古易慎戈廖庚终暨居衡步都耿满弘匡国文寇广禄阙东殴殳沃利蔚越夔隆师巩厍聂晁勾敖融冷訾辛阚那简饶空曾沙须丰巢关蒯相查后江游竺",3);

    }

    public function run($str = '', $encode = 'UTF-8', $algorithm = 2)
    {
        if($str == '') {
            return '';
        };

        $this->algorithm = $algorithm;
        $this->str = $str.'!';
        $this->encode = $encode != '' ? $encode : 'UTF-8';
        $this->length = mb_strlen($this->str,$this->encode);

        $this->start();
        $this->re_treatment($this->keywords);
        return $this->keywords;
    }

    /**
     * 开始分词
     */
    public function start()
    {
        $start = 0;
        $en_start = -1;
        $num_start = -1;
        $this->length = mb_strlen($this->str,$this->encode);
        $flag = 0;
        for($i = 0; $i < $this->length; $i++) {
            $ch = $this->_substr($this->str,$i,1);
            if($ch == ' ') { //替换中文空格
                $ch ='';
            }
            if(in_array($ch,$this->split_arr) || $flag) { //分隔符
                $flag = 0;
                if($i - $start > 1 && ($tmp = trim($this->_substr($this->str,$start,$i - $start)))) {
                    $this->split_word($tmp);
                }
                $start = $i;
            } elseif(preg_match('/[a-zA-Z]/', $ch)){ //英文

                if($en_start == -1) {
                    $one_line = $this->_substr($this->str,$start,$i - $start);
                    if(trim($one_line) != '' && !in_array($one_line,$this->split_arr)) {
                        $this->split_word($one_line); //算前面的
                    }
                }

                if($en_start == -1) {
                    $en_start = $i;
                } elseif(!preg_match('/[a-zA-Z]/', $this->_substr($this->str, $i + 1, 1))) {
                    $this->keywords[] = array($this->_substr($this->str,$en_start,$i - $en_start + 1));
                    $i++;
                    $start = $i;
                    $en_start = -1;
                }
            }
        }

    }

    public function Split_word($str)
    {
        if($this->algorithm == 0) {
            $this->keywords[] = $this->PMMA($str);
        } elseif($this->algorithm == 1) {
            $this->keywords[] = array_reverse($this->RMMA($str));
        } elseif($this->algorithm == 2) {
            $pmma = $this->PMMA($str);
            $rmma = $this->RMMA($str);

            /**
             * 比较两种算法 词数越少越好
             * 总计频率越高越好
             */
            if(count($pmma) <= count($rmma)) {
                $pmmqaa['type'] = 0;
                $this->keywords[] = $pmma;
            } else {
                $rmma['type'] = 1;
                $this->keywords = $rmma;
            }
        }
    }

    /**
     * 正向最大匹配算法
     * @param $str
     */
    public function PMMA($str)
    {
        $start = 0;
        $len = mb_strlen($str,$this->encode);
        $tmp = array();
        while($start < $len) {
            for($i = ($len >= self::MAXLEN ? self::MAXLEN : $len); $i >0; $i--) {
                $str_i = $this->_substr($str,$start,$i);

                if($this->_query("select * from words where len =".$i." and name = '".$str_i."'") || $i == 1) {
                    $start += $i;
                    $tmp[] = $str_i;
                    break;
                }
            }
        }
        return $tmp;
    }

    /**
     * 逆向最大匹配算法
     * @param $str
     */
    public function RMMA($str)
    {
        $len = mb_strlen($str,$this->encode);
        $start = 0;
        $tmp = array();
        while($start < $len) {
            for($i = ($len - $start > self::MAXLEN ? self::MAXLEN : $len - $start); $i > 0; $i--) {
                $str_i = $this->_substr($str,-$i-$start,$i);
                if($this->_query("select * from words where len =".$i." and name = '".$str_i."'") || $i == 1) {
                    $start += $i;
                    array_push($tmp,$str_i);
                    break;
                }
            }
        }
        return $tmp;
    }



    /**
     * 查找词典
     * @param $sql
     * @return array|bool
     */
    private function _query($sql)
    {
        $result = mysql_query($sql);
        if( ($row = mysql_fetch_row($result))) {
            return $row;
        }
        return false;
    }

    /**
     * 处理keyword
     * 0:识别姓名 1,数字+单位
     */
    private function re_treatment()
    {
        $num_unit = -1;
        foreach($this->keywords as $key=>&$kw) { //$kw为一句
            for($i = 0; $i < (end(array_keys($kw)) == 'type' ? end(array_keys($kw)) : end(array_keys($kw))) + 1; $i++) {

                /**
                 * 姓名识别
                 */
                if(preg_match('/[\x{4e00}-\x{9fa5}]/u',$kw[$i])) {
                    $max_len = mb_strlen($kw[$i],$this->encode) == 1 ? 3 : 4;
                    if($max_len == 3 ? in_array($kw[$i],$this->one_name) : in_array($kw[$i],$this->two_name)) {
                        $len = end(array_keys($kw)) ;
                        if(($n = ($len - $i) >=2 ? 2 : ($len - $i) ) >= 1) {
                            for($j = 1; $j <= $n; $j++) {
                                if($i + 1 <= $len && mb_strlen($kw[$i].$kw[$i+1],$this->encode) <= $max_len) {
                                    $kw[$i+1] =$kw[$i].$kw[$i+1];
                                    unset($kw[$i]);
                                    $i += 1;
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                }

                /**
                 * 识别数字+单位
                 */
                if(preg_match('/\d/',$kw[$i])) {
                    $num_unit = $num_unit != -1 ? $num_unit : $i;
                } elseif(preg_match('/[\x{4e00}-\x{9fa5}]/u',$kw[$i]) && in_array($kw[$i],$this->unit_arr) && $num_unit != -1) {
                    $tmp = $num_unit++;
                    while($num_unit  <= $i) {
                        $kw[$tmp] .= $kw[$num_unit];
                        unset($kw[$num_unit]);
                        $num_unit++;
                    }
                    $num_unit = -1;
                }

            }

        }


    }

    /**
     * 打印分词后的句子
     */
    public function print_splited_word()
    {
        foreach($this->keywords as $key=>$kw) {
            if(isset($kw['type']) && $kw['type'] == 1) {
                array_reverse($kw);
            }
            foreach($kw as $k => $v) {
                if($k>=0 && ! in_array($v,$this->split_arr))  { //去掉符号 换行打印
                    echo $v." / ";
                }
            }
        }
    }

    /**
     * 截取字符串
     * @param $str
     */
    private function _substr($str, $start, $len)
    {
        return mb_substr($str,$start,$len,$this->encode);
    }


}